/*
 * Copyright (c) Huawei Device Co., Ltd. 2023. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "access_rule/access_control_enforcer.h"

#include <dlfcn.h>
#include "../utils/application_parser.h"
#include "loghelper.h"
#include "access_rule_cache.h"
#include "bundle_mgr_interface.h"
#include "common_utils.h"
#include "sha256_utils.h"

static const std::string EXT_SE_SERVICE_LIB = "libext_se_service.z.so";
static const std::string EXT_CHECK_BUNDLE_NAME_PERMISSION = "CheckBundleNamePermission";
static const std::string EXT_IS_SYSTEM_UID = "IsSystemUid";

namespace OHOS {
namespace SE {
using namespace OHOS::AppExecFwk;
sptr<AppExecFwk::IBundleMgr> bundleMgrProxy_;
const int BUNDLE_MGR_SERVICE_SYS_ABILITY_ID = 401;
AccessControlEnforcer::AccessControlEnforcer(std::weak_ptr<SecureElementAgent> agent)
{
    isAra_ = true;
    isArf_ = true;
    agent_ = agent;
}

AccessControlEnforcer::AccessControlEnforcer(bool isAra, bool isArf, std::weak_ptr<SecureElementAgent> agent)
{
    isAra_ = isAra;
    isArf_ = isArf;
    agent_ = agent;
}

AccessControlEnforcer::~AccessControlEnforcer()
{
}

bool AccessControlEnforcer::CheckBundleNamePermission(const std::string& bundleName)
{
    bool ret = false;
    nfcExtInf_.checkBundleNamePermission = reinterpret_cast<bool (*)(const char*)>
        (dlsym(nfcExtHandle_, EXT_CHECK_BUNDLE_NAME_PERMISSION.c_str()));
    if (nfcExtInf_.checkBundleNamePermission == nullptr) {
        ErrorLog("fail to init func ptr.");
        return ret;
    }
    ret = nfcExtInf_.checkBundleNamePermission(bundleName.c_str());
    return ret;
}

bool AccessControlEnforcer::IsSystemUid(const uint32_t& uid)
{
    bool ret = false;
    nfcExtInf_.isSystemUid = reinterpret_cast<bool (*)(const uint32_t)>
        (dlsym(nfcExtHandle_, EXT_IS_SYSTEM_UID.c_str()));
    if (nfcExtInf_.isSystemUid == nullptr) {
        ErrorLog("fail to init func ptr.");
        return ret;
    }
    ret = nfcExtInf_.isSystemUid(uid);
    return ret;
}

bool AccessControlEnforcer::IsChannelAccessAllowed(const uint32_t& uid)
{
    // load ext so
    if (nfcExtHandle_ == nullptr) {
        nfcExtHandle_ = dlopen(EXT_SE_SERVICE_LIB.c_str(), RTLD_LAZY | RTLD_GLOBAL);
    }

    if (nfcExtHandle_ == nullptr) {
        ErrorLog("fail to get nfc ext service handle.");
        return false;
    }

    std::string bundleName = ApplicationParser::GetBundleNameByUid(uid);
    if (CheckBundleNamePermission(bundleName) && ApplicationParser::IsSystemApplication(uid)) {
        return true;
    } else if (IsSystemUid(uid)) {
        return true;
    }
    InfoLog("IsChannelAccessAllowed (%{public}d)%{public}s isn't systemApp or not in whiteList",
        uid, bundleName.c_str());
    return false;
}

sptr<AppExecFwk::IBundleMgr> AccessControlEnforcer::GetBundleMgrProxy()
{
    sptr<ISystemAbilityManager> systemAbilityManager =
        SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
    if (!systemAbilityManager) {
        ErrorLog("GetBundleMgrProxy, systemAbilityManager is null");
        return nullptr;
    }
    sptr<IRemoteObject> remoteObject = systemAbilityManager->GetSystemAbility(BUNDLE_MGR_SERVICE_SYS_ABILITY_ID);
    if (!remoteObject) {
        ErrorLog("GetBundleMgrProxy, remoteObject is null");
        return nullptr;
    }
    return iface_cast<AppExecFwk::IBundleMgr>(remoteObject);
}

bool AccessControlEnforcer::GetBundleInfo(const std::string &bundleName, AppExecFwk::BundleInfo &bundleInfo)
{
    int32_t userId = 100;
    if (bundleMgrProxy_ == nullptr) {
        bundleMgrProxy_ = GetBundleMgrProxy();
    }
    if (bundleMgrProxy_ == nullptr) {
        ErrorLog("bundleMgrProxy_ is nullptr.");
        return false;
    }
    bool result = bundleMgrProxy_->GetBundleInfoV9(bundleName,
        static_cast<int32_t>(OHOS::AppExecFwk::GetBundleInfoFlag::GET_BUNDLE_INFO_WITH_APPLICATION) |
        static_cast<int32_t>(OHOS::AppExecFwk::GetBundleInfoFlag::GET_BUNDLE_INFO_WITH_SIGNATURE_INFO),
        bundleInfo, userId);
    InfoLog("get bundle %{public}s result %{public}d ", bundleName.c_str(), result);
    if (!result) {
        ErrorLog("get bundle %{public}s failed ", bundleName.c_str());
        return false;
    }
    return true;
}

bool AccessControlEnforcer::IsAllowedByAidAndAppId(const std::vector<uint8_t> &aid, const uint32_t& uid)
{
    std::string bundleName = ApplicationParser::GetBundleNameByUid(uid);
    InfoLog("IsAllowedByAidAndAppId enter bundlename = %{public}s", bundleName.c_str());
    accessCache_  = std::make_shared<AccessRuleCache>(isAra_, isArf_, agent_);
    if (accessCache_ == nullptr || !accessCache_->Initialize()) {
        ErrorLog("fail to init accessCache_.");
        return false;
    }
    AppExecFwk::BundleInfo bundleInfo;
    GetBundleInfo(bundleName, bundleInfo);
    std::string appIdentifierVal = bundleInfo.signatureInfo.appIdentifier;
    std::string fingerPrint = bundleInfo.signatureInfo.fingerprint;
    std::string appIdentifier = Sha256Utils::CalculateHashCode(appIdentifierVal);
    return accessCache_->IsAllowed(aid, appIdentifier, fingerPrint);
}

bool AccessControlEnforcer::IsAllowedByFingerPrint(uint32_t uid)
{
    std::string bundleName = ApplicationParser::GetBundleNameByUid(uid);
    InfoLog("calling bundleName = %{public}s", bundleName.c_str());
    AppExecFwk::BundleInfo bundleInfo;
    GetBundleInfo(bundleName, bundleInfo);
    std::string fingerPrint = bundleInfo.signatureInfo.fingerprint;
    return accessCache_->IsAllowedByFingerPrint(fingerPrint);
}
}  // namespace SE
}  // namespace OHOS
